import anime from 'animejs'
import { ElInput, ElMessageBox } from 'element-plus'
import { PointerEvent } from 'leafer-ui'
import { h, ref } from 'vue'
import { edgeConfig } from '../config/edgeConfig'
import { RamInitialData } from '../config/initialData'
import { sizeConfig } from '../config/sizeConfig'
import { BoolType, IModuleWithState } from '../types'
import { uiItems } from '../uiItems'
import {
    $,
    createDataValue,
    getCenterPoint,
    setDomBoolVal,
    toBinaryStr,
    toInt255,
} from '../utils'
import { bus1, busRamToMultiplexer31RAI } from './bus'
import { getInsCode, getInstruction } from './decoder'

interface IRamState {
    re: BoolType
    wr: BoolType
    selAddr: number
}

interface IRam extends IModuleWithState<IRamState> {
    data: number[]
    setRamData: (addr: number, value: number) => Promise<void>
}

// 把数组的长度设置成256
for (let i = RamInitialData.length; i < 256; i++) {
    RamInitialData[i] = 0
}

/**
 * 创建一个ramItem的dom
 */
function createRamItemDiv(addr: number, val: number): HTMLElement {
    const div = document.createElement('div')
    div.classList.add('ram-item')
    div.setAttribute('id', `ram-item-${addr}`)

    const addrDiv = document.createElement('div')
    addrDiv.classList.add('ram-item-addr')
    addrDiv.classList.add('data-item-val')
    addrDiv.dataset.value = addr + ''
    addrDiv.textContent = toBinaryStr(addr)

    const valDiv = document.createElement('div')
    valDiv.classList.add('ram-item-val')
    valDiv.classList.add('data-item-val')
    valDiv.dataset.value = val + ''
    valDiv.textContent = toBinaryStr(val)

    div.appendChild(addrDiv)
    div.appendChild(valDiv)

    return div
}

/**
 * 处理编辑ram逻辑
 */
function initEditRam() {
    const dom = uiItems.ram
    if (!dom) return
    dom.cursor = 'pointer'
    dom.on(PointerEvent.TAP, () => {
        const txtVal = ram.data.reduce((prev, curr, index) => {
            let suf = ''
            const ins = getInstruction(curr)
            if (ins.isIns) {
                suf = ins.fullIns
            } else {
                suf = toBinaryStr(curr, 8, true)
            }
            return prev + index + ': ' + suf + '\n'
        }, '')
        const txtValRef = ref(txtVal)
        ElMessageBox({
            showClose: true,
            showCancelButton: true,
            confirmButtonText: '确定',
            title: '编辑RAM',
            customStyle: { width: '800px' },
            beforeClose: (action, _, done) => {
                if (action === 'confirm') {
                    const value = txtValRef.value
                    // 以换行或‘，’分割
                    const values = value.split(/,|\n/)
                    values.forEach((item: string, index: number) => {
                        item = item.trim()
                        if (item === '') {
                            return
                        }
                        // 判断item是否包含冒号
                        const colonIndex = item.indexOf(':')
                        let addr: string | number = index
                        let val = item
                        if (colonIndex !== -1) {
                            // 包含冒号
                            const splits = item.split(':')
                            addr = splits[0].trim()
                            val = splits[1].trim()
                        }
                        const addrInt = toInt255(addr + '')
                        const valInt = toInt255(getInsCode(val))
                        if (ram.data[addrInt] !== valInt) {
                            ram.setRamData(addrInt, valInt)
                        }
                    })
                }
                done()
            },
            message: () =>
                h('div', { class: 'w-[800px]' }, [
                    h(
                        'p',
                        null,
                        '编辑RAM的内容，每一条内容以 "," 或 换行 隔开'
                    ),
                    h(
                        'p',
                        null,
                        '如果将内容写入到指定地址请使用 ":" ，例如"1:123"'
                    ),
                    h(
                        'p',
                        null,
                        '输入指令时前四位是大写英文字母，再用一个空格隔开写后四位'
                    ),
                    h('p', null, '例如："MOVA 0101"，"IN"等'),
                    h(ElInput, {
                        type: 'textarea',
                        rows: 10,
                        modelValue: txtValRef.value,
                        'onUpdate:modelValue': (v) => (txtValRef.value = v),
                    }),
                ]),
        })
    })
    return
}

/**
 * ram模块
 */
export const ram: IRam = {
    name: 'ram',
    data: [],
    state: {
        re: 0,
        wr: 1,
        selAddr: 0,
    },
    async setState(prop, value) {
        if (prop === 're' || prop === 'wr') {
            this.state[prop] = value
            setDomBoolVal(prop, this.state[prop] as BoolType)
        } else if (prop === 'selAddr') {
            // 将上一个选中的地址的样式去掉
            const oldSelAddr = this.state.selAddr
            this.state[prop] = value
            $(`#ram-item-${oldSelAddr}`)?.classList.remove('selected')
            $(`#ram-item-${value}`)?.classList.add('selected')
            // 让这个ram-item滑动到顶部
            const offsetTop = $(`#ram-item-${oldSelAddr}`)?.offsetTop
            if (offsetTop) {
                $('.ram .ram-items')?.scrollTo({
                    top: offsetTop - 20,
                    behavior: 'smooth',
                })
            }
        }
        return
    },
    async setRamData(addr: number, value: number) {
        this.data[addr] = value
        const valDiv = $(`#ram-item-${addr} .ram-item-val`)
        if (valDiv) {
            valDiv.textContent = toBinaryStr(value)
            valDiv.dataset.value = value + ''
        }
    },
    async rise() {
        // 在时钟上升沿
        if (this.state.re === 1) {
            // 当re为1时，把当前地址的数据给到1号总线
            const currIndex = this.state.selAddr
            const selValue = this.data[currIndex]
            const text = toBinaryStr(selValue)
            const data = createDataValue(selValue, text, {
                x: sizeConfig.ram.x + 60,
                y: sizeConfig.ram.y + 10 * (currIndex > 5 ? 5 : currIndex) + 10,
            })
            // 从初始位置到达ram的顶部后消失
            const timeline = anime.timeline({
                targets: data.dom,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
                easing: 'linear',
            })
            const ramTopCenter = getCenterPoint(sizeConfig.ram, 'top')
            return timeline.add({
                x: ramTopCenter.x + sizeConfig.ram.width / 4,
                y: ramTopCenter.y - 10,
                complete: () => {
                    busRamToMultiplexer31RAI.setData(selValue)
                    data.dom.remove()
                },
            }).finished
        } else if (this.state.wr === 1) {
            // 当wr为1时，从1号总线中拿数据放到当前地址
            const currIndex = this.state.selAddr
            const busValue = bus1.getData()
            const text = toBinaryStr(busValue)
            const ramTopCenter = getCenterPoint(sizeConfig.ram, 'top')
            const data = createDataValue(busValue, text, {
                x: sizeConfig.ram.x + 60,
                y: ramTopCenter.y - 10 - 34,
            })
            // 从初始位置到达ram的顶部后消失
            const timeline = anime.timeline({
                targets: data.dom,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
                easing: 'linear',
            })
            return timeline.add({
                y: sizeConfig.ram.y + 10 * (currIndex > 5 ? 5 : currIndex) + 10,
                complete: () => {
                    this.setRamData(currIndex, busValue)
                    data.dom.remove()
                },
            }).finished
        }
    },
    init() {
        // 设置re的初始值
        setDomBoolVal('re', this.state.re)
        // 设置wr的初始值
        setDomBoolVal('wr', this.state.wr)
        // 根据data的内容，初始化内存，显示到页面上
        const fragment = document.createDocumentFragment()
        for (let i = 0; i < RamInitialData.length; i++) {
            this.data[i] = RamInitialData[i]
            fragment.appendChild(createRamItemDiv(i, RamInitialData[i]))
        }
        $('.ram .ram-items')?.appendChild(fragment)
        // 设置当前选中的地址
        $(`#ram-item-${this.state.selAddr}`)?.classList.add('selected')
        // bus1.setData(this.data[this.state.selAddr], 'ram')
        initEditRam()
    },
}
